package com.push.common.utils;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import org.apache.log4j.Logger;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import net.sourceforge.tess4j.util.LoadLibs;
import java.security.MessageDigest;
import java.math.BigInteger;

public class ImageUtil {
    private static Logger log = Logger.getLogger(ImageUtil.class);
    /**
     * 将网络图片进行Base64位编码
     *
     * @param imageUrl
     *            图片的url路径，如http://.....xx.jpg
     * @return
     */
    public static String encodeImgageToBase64(URL imageUrl) {// 将图片文件转化为字节数组字符串，并对其进行Base64编码处理
        ByteArrayOutputStream outputStream = null;
        try {
            BufferedImage bufferedImage = ImageIO.read(imageUrl);
            outputStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "jpg", outputStream);
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 对字节数组Base64编码
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(outputStream.toByteArray());// 返回Base64编码过的字节数组字符串
    }

    /**
     * 将本地图片进行Base64位编码
     *
     * @param imageFile
     *            图片的url路径，如http://.....xx.jpg
     * @return
     */
    public static String encodeImgageToBase64(File imageFile) {// 将图片文件转化为字节数组字符串，并对其进行Base64编码处理
        ByteArrayOutputStream outputStream = null;
        try {
            BufferedImage bufferedImage = ImageIO.read(imageFile);
            outputStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "jpg", outputStream);
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 对字节数组Base64编码
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(outputStream.toByteArray());// 返回Base64编码过的字节数组字符串
    }

    /**
     * 将Base64位编码的图片进行解码，并保存到指定目录
     *
     * @param base64
     *            base64编码的图片信息
     * @return
     */
    public static void decodeBase64ToImage(String base64, String path, String imgName) {
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            FileOutputStream write = new FileOutputStream(new File(path + imgName));
            byte[] decoderBytes = decoder.decodeBuffer(base64);
            write.write(decoderBytes);
            write.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 将Base64位编码的图片进行解码，并保存到指定目录
     *
     * @param path
     *            图片路径
     * @return
     */
    public static String identifyCode(String path) {
        // 创建实例
        //ITesseract instance = new Tesseract();
        //相对目录，这个时候tessdata目录和src目录平级
        //instance.setDatapath("D:\\temp\\tessdata");
        //选择字库文件（只需要文件名，不需要后缀名）
        //instance.setLanguage("eng");
        try {
            //File imageFile = new File(path);
            //开始识别
            //String result = instance.doOCR(imageFile);
            //打印图片内容
            //System.out.println("result="+result);
            ITesseract instance = new Tesseract();
            instance.setDatapath("D:\\temp\\tessdata");
            //instance.setLanguage("chi_sim");
            File imgDir = new File(path);
            //long startTime = System.currentTimeMillis();
            String result = instance.doOCR(imgDir);
            System.out.println("result="+result);

            return result;
        } catch (Exception e) {
            //打印图片内容
            System.out.println(e.toString());
            return "";
        }
    }

    /**
     * 读取验证码
     *     1、去除验证码图片中的干扰信息
     *     2、把背景改为纯白色
     *     3、把文字改为纯黑色
     *     4、读取验证码
     * @param imagePath 原图片本地保存路径
     * @return 验证码
     * @throws Exception
     */
    public static String readVerifyImage(String imagePath) throws Exception {
        log.debug("验证码原文件：" + imagePath);
        // 处理图片
        String outImage = dealImage(imagePath);

        // windows和linxu的API路径不同，需要单独处理
        File tessDataFolder = LoadLibs.extractTessResources("tessdata");
        String tessdata = tessDataFolder.getAbsolutePath();
        if(System.getProperty("os.name").toLowerCase().contains("linux")) {
            tessdata.replace("tessdata", "");
        }

        // 读取验证码
        Tesseract instance = new Tesseract();
        System.out.println(tessdata);
        instance.setDatapath(tessdata);
        instance.setTessVariable("user_defined_dpi", "300");
        String verification = instance.doOCR(new File(outImage));
        verification = verification.replaceAll("[^0-9a-zA-Z]","");
        return verification;
    }
    /**
     * 处理图片
     * 		其实可以不对图片做处理，直接使用Tess4j直接读取图片文字。
     * 		不过不经过图片处理的图片识别率较低，大概只有10%的成功率。
     * 		经过处理的图片，识别率提高到了50%左右。
     * @param imagePath 图片的绝对或相对路径
     * @return 处理后的图片保存路径
     * @throws IOException
     */
    public static String dealImage(String imagePath) throws IOException {
        String formatName = imagePath.substring(imagePath.indexOf(".") + 1);
        File file = new File(imagePath);
        BufferedImage image = ImageIO.read(file);

        int width = image.getWidth();
        int height = image.getHeight();

        BufferedImage outImage = new BufferedImage(width, height, image.getType());
        int backgroudColor = image.getRGB(0, 0);
        int backgroudR = (backgroudColor >> 16) & 0xff;
        int backgroudG = (backgroudColor >> 8) & 0xff;
        int backgroudB = backgroudColor & 0xff;
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                int color = image.getRGB(i, j);
                int r = (color >> 16) & 0xff;
                int g = (color >> 8) & 0xff;
                int b = color & 0xff;
                int newColor = color;

                // 去除干扰信息，干扰信息为黑色相近46/256之内全部清理
                if(r < 64 && g < 64 && b < 64) {
                    if(j-1 >= 0) {
                        newColor = image.getRGB(i, j-1);
                    } else if(i-1 >= 0) {
                        newColor = image.getRGB(i-1, j);
                    } else if(j+1 < height) {
                        newColor = image.getRGB(i, j+1);
                    } else if(i+1 < width) {
                        newColor = image.getRGB(i+1, j);
                    }
                    r = (newColor >> 16) & 0xff;
                    g = (newColor >> 8) & 0xff;
                    b = newColor & 0xff;
                }

                // 去除背景颜色，相近的±30之内的全部设置为白色，灰色的干扰信息改为白色，文字改为黑色
                if(Math.abs((r - backgroudR)) <= 30 && Math.abs((g - backgroudG)) <= 30 && Math.abs((b - backgroudB)) <= 30) {
                    newColor = 0xffffff;
                } else if(r > 150 && g > 150 && b > 150){
                    newColor = 0xffffff;
                } else {
                    newColor = 0x000000;
                }
                outImage.setRGB(i, j, newColor);
            }
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(outImage, formatName, out);
        String outPath = new File(imagePath).getParent() + File.separator + getFileMd5(out.toByteArray()) + "." + formatName;
        File newFile = new File(outPath);
        ImageIO.write(outImage, formatName, newFile);
        log.debug("处理后的验证码文件：" + outPath);

        return outPath;
    }

    /**
     * 根据文件字节流获取文件MD5
     * @param fileBytes
     * @return
     */
    public static String getFileMd5(byte[] fileBytes) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] mdBytes = md.digest(fileBytes);
            BigInteger bigInt = new BigInteger(1, mdBytes);
            return bigInt.toString(16);
        } catch (Exception e) {
            log.error("删除文件失败",e);
            return null;
        }
    }
}
